using System;
using System.Collections.Generic;
using System.Text;
using System.Transactions;
using SimpleFS.Core;

[assembly: CLSCompliant(true)]
namespace SimpleFS.Services.Transactions.Implementations
{
    public sealed class SimpleResourceManager : IResourceManager
    {
        internal Stack<ITransactableOperation> undoLog = null;

        internal Queue<ITransactableOperation> redoLog = null;

        internal Transaction transaction;

        internal Transaction lastTransaction;

        public SimpleResourceManager()
        {
            this.undoLog = new Stack<ITransactableOperation>();
            this.redoLog = new Queue<ITransactableOperation>();
        }

        public void WriteRedoLog(ITransactableOperation operation)
        {
            this.redoLog.Enqueue(operation);
        }

        public void WriteUndoLog(ITransactableOperation operation)
        {
            this.undoLog.Push(operation);
        }

        /// <summary>
        /// Start to participate into an external transaction.
        /// </summary>
        public void Enlist()
        {
            this.transaction = Transaction.Current;
            if (this.transaction == null)
            {
                //throw new ApplicationException("There is currently no transaction running");
                // do something...
            }
            else
            {
                if (this.lastTransaction != null)
                {
                    if (this.lastTransaction != this.transaction)
                    {
                        Console.WriteLine("Cleans log!");
                        this.redoLog.Clear();
                        this.undoLog.Clear();
                    }
                }
                else
                {
                    this.lastTransaction = this.transaction;
                }

                this.transaction.EnlistVolatile(this, EnlistmentOptions.None);
            }
        }

        public bool IsEnlisted()
        {
            return this.transaction != null;
        }

        #region IEnlistmentNotification
        void IEnlistmentNotification.InDoubt(Enlistment enlistment)
        {
            // does nothing
        }

        void IEnlistmentNotification.Prepare(PreparingEnlistment preparingEnlistment)
        {
            preparingEnlistment.Prepared();
        }

        void IEnlistmentNotification.Commit(Enlistment enlistment)
        {
            ITransactableOperation operation = null;
            while (this.redoLog.Count > 0)
            {
                operation = redoLog.Dequeue();
                operation.Commit();
            }
            enlistment.Done();
            Console.WriteLine("Committed");
        }

        void IEnlistmentNotification.Rollback(Enlistment enlistment)
        {
            ITransactableOperation operation = null;
            while (this.undoLog.Count > 0)
            {
                operation = this.undoLog.Pop();
                operation.Rollback();
            }
            enlistment.Done();
            Console.WriteLine("Rollbacked");
        }
        #endregion

        public void Dispose()
        {
            this.transaction = null;
        }
    }
}
